/*
 * ----------------------------------------------------------------------- Copyright 2012 - Alistair
 * Rutherford - www.netthreads.co.uk
 * -----------------------------------------------------------------------
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.garrigou.spaceship.sprite;

import aurelienribon.tweenengine.BaseTween;
import aurelienribon.tweenengine.Timeline;
import aurelienribon.tweenengine.Tween;
import aurelienribon.tweenengine.TweenCallback;
import aurelienribon.tweenengine.equations.Linear;

import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.garrigou.spaceship.action.ActionCallBack;
import com.garrigou.spaceship.action.CallBackDelayAction;
import com.garrigou.spaceship.action.TimelineAction;
import com.garrigou.spaceship.actor.Ship2D;
import com.garrigou.spaceship.constants.ActorEvents;
import com.garrigou.spaceship.constants.AppStates;
import com.garrigou.spaceship.director.Director;
import com.garrigou.spaceship.scenes.SceneHelper;
import com.garrigou.spaceship.tween.ActorAccessor;
import com.garrigou.spaceship.tween.GroupAccessor;

/**
 * Represent an asteroid sprite.
 * 
 * An asteroid rotates at a random speed as it moves from it's starting point to the bottom.
 * 
 * An asteroid checks for collision with other actors and generates the appropriate event.
 * 
 */
public class AsteroidSprite extends AnimatedSprite implements TweenCallback, ActionCallBack
{
  private static final int   MIN_DURATION      = 2;
  private static final int   MAX_DURATION      = 10;
  private static final float CALLBACK_INTERVAL = 0.05f; // 50 milliseconds

  // Director of the action.
  private Director           director;

  /**
   * Create sprite.
   * 
   * @param texture
   *          The animation texture.
   * @param rows
   *          The animation texture rows.
   * @param cols
   *          The animation texture rows.
   * @param frameDuration
   *          The animation frame duration.
   */
  public AsteroidSprite(TextureRegion textureRegion, int rows, int cols, float frameDuration)
  {
    super(textureRegion, rows, cols, frameDuration);

    this.director = Director.instance();

    // Move origin to the centre so that rotation works from there.
    setOrigin(getWidth() / 2, getHeight() / 2);
  }

  /**
   * Run actions for actor.
   * 
   * 
   * @return The main actions object.
   */
  public void run()
  {
    float width = Director.instance().getWidth();
    float height = Director.instance().getHeight();
    float randomX = (float) (Math.random() * width);

    // From the top and random point across the width.
    setPosition(randomX, height);

    // ---------------------------------------------------------------
    // Animation.
    // ---------------------------------------------------------------
    int durationMove = (int) ((Math.random() * MAX_DURATION + MIN_DURATION) * 1000);
    int rotate = (int) (Math.random() * 1440) - 720;

    Timeline timeline = Timeline
        .createParallel()
        .beginParallel()
        .push(Tween.to(this, ActorAccessor.POSITION_XY, durationMove)
                  .setCallbackTriggers(TweenCallback.BEGIN).target(getX(), 0 - getHeight())
                  .ease(Linear.INOUT)).beginSequence()
        .push(Tween.to(this, GroupAccessor.ROTATION, 0).target(0))
        .push(Tween.to(this, GroupAccessor.ROTATION, durationMove).target(rotate)).end().end()
        .setCallbackTriggers(TweenCallback.COMPLETE).setCallback(this).start();

    TimelineAction timelineAction = TimelineAction.$(timeline);
    addAction(timelineAction);

    // ---------------------------------------------------------------
    // CallBack
    // ---------------------------------------------------------------
    CallBackDelayAction callBackDelay = CallBackDelayAction.$(-1, CALLBACK_INTERVAL, this);
    addAction(callBackDelay);
  }

  /**
   * Action callback.
   * 
   */
  @Override
  public void onCallBack()
  {
    handleCheck();
  }

  /**
   * Handles pulse action complete.
   * 
   */
  @Override
  public void onEvent(int eventType, BaseTween<?> source)
  {
    switch (eventType)
    {
      case BEGIN:
        setRotation(0);
        break;
      case COMPLETE:
        handleComplete();
        break;
      default:
        break;
    }
  }

  /**
   * Handles pulse action complete.
   * 
   */
  private void handleComplete()
  {
    // Send notification that pulse has completed.
    this.director.sendEvent(ActorEvents.EVENT_END_ASTEROID, this);
  }

  /**
   * Triggered on call back action.
   * 
   */
  private void handleCheck()
  {
    // Check for collision with other actor.
    Actor target = collisionCheck();

    if (target != null)
    {
      if (target instanceof Ship2D)
      {
        // Don't run collision or anything else if target is busy.
//        if (!AppStates.shipSpriteBusy)
        {
          // Ship collision.
          this.director.sendEvent(ActorEvents.EVENT_COLLISION_ASTEROID_SHIP, this);
        }
      }
      // else if (target instanceof PulseSprite)
      // {
      // // Asteroid explode.
       this.director.sendEvent(ActorEvents.EVENT_COLLISION_ASTEROID_PULSE, this);
      //
      // // Pulse finished.
      // this.director.sendEvent(ActorEvents.EVENT_END_PULSE, target);
      // }
    }
  }

  /**
   * Check for collision with another actor on the stage.
   * 
   * @return Target object if collision.
   */
  private Actor collisionCheck()
  {
    Actor actor = null;

    Stage stage = getStage();

    if (stage != null)
    {

      // TODO : finish it
      // Have we hit a pulse?
      // actor = SceneHelper.intersects(getX(), getY(), getWidth(), getHeight(), stage.getRoot(),
      // PulseSprite.class);

      if (actor == null)
      {
        actor = SceneHelper.intersects(getX(), getY(), getWidth(), getHeight(), stage.getRoot(),
                                       Ship2D.class);
      }
    }

    return actor;
  }

  /**
   * Override the hit method here to ensure our collision detection helper class {@link SceneHelper}
   * can make a match against an actual game element like this Asteroid class. I.e if you call the
   * super-class method you will get a match against a FrameSprite which isn't what we want.
   */
  @Override
  public Actor hit(float x, float y)
  {
    return x > 0 && x < getWidth() && y > 0 && y < getHeight() ? this : null;
  }

}
